package com.watson.mongo.jzw.utils;

import cn.hutool.core.lang.Console;
import com.mongodb.BasicDBObject;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.regex.Pattern;

/**
 * @author xl
 * @date 2021/4/30
 * mongodb的增删改查工具类
 */
@Component
public class MongodbUtils {
    public static MongodbUtils mongodbUtils;
    /**
     * 线程池创建
     */
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 200, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(100000), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

    @PostConstruct
    public void init() {
        mongodbUtils = this;
        mongodbUtils.mongoTemplate = this.mongoTemplate;
    }

    @Resource
    private MongoTemplate mongoTemplate;


    /**
     * 查询出所有结果集 集合为数据对象中@Document 注解所配置的collection
     *
     * @param clazz 泛型
     * @return List
     */
    public static <T> List<T> findAll(Class<T> clazz) {
        List<T> resultList = mongodbUtils.mongoTemplate.findAll(clazz);
        return resultList;
    }

    /**
     * 分页查找某个子集元素的长度大于等于1，并且不为null
     *
     * @param childName 子集元素的名称
     * @param clazz     类型
     * @param <T>       泛型
     * @return 数据集合
     */
    public <T> List<T> findChildDocumentGtOne(Integer limit, Integer page, String childName, Class<T> clazz) {
        List<T> resultList = null;
        try {
            resultList = mongodbUtils.mongoTemplate.find(Query.query(Criteria.where(childName + ".0").exists(true).and(childName).exists(true)).limit(limit).skip(limit * (page - 1)), clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultList;
    }


    /**
     * 分页查询 集合为数据对象中@Document 注解所配置的collection
     *
     * @param page  当前页
     * @param limit 分页条数
     * @return 数据
     */
    public <T> Map<String, Object> findAllByPage(Class<T> clazz, Integer page, Integer limit) {
        Map<String, Object> map = new HashMap<>();
        page = page - 1;
        Query query = new Query();
        //根据集合中对象的某个字段排序
        query.with(Sort.by(
                Sort.Order.desc("sort")
        ));
        query.addCriteria(Criteria.where("isDel").ne("1"));
        if (page <= 1000) {
            long count = mongodbUtils.mongoTemplate.count(query, clazz);
            List<T> ts = mongodbUtils.mongoTemplate.find(query.limit(limit).skip(page * limit), clazz);
            map.put("data", ts);
            map.put("count", count);
            return map;
        } else {
            //总量
            CompletableFuture<Long> future1 = CompletableFuture.supplyAsync(() -> {
                long l = System.currentTimeMillis();
                //总量
                Query query1 = new Query();
                long all = mongodbUtils.mongoTemplate.count(query1, clazz);
                //被删除的有多少
                Query query2 = new Query();
                long isDel = mongodbUtils.mongoTemplate.count(query2.addCriteria(Criteria.where("isDel").ne("0")), clazz);
                long count = all - isDel;
                Console.log("总量耗时" + (System.currentTimeMillis() - l));
                return count;
            }, executor);


            Integer finalPage = page;
            CompletableFuture<List<T>> future2 = CompletableFuture.supplyAsync(() -> {
                long l = System.currentTimeMillis();
                List<T> resultList = new ArrayList<>();
                Long sort = 0L;
                //获取最大值的sort
                T one = this.mongoTemplate.findOne(query, clazz);
                if (one == null) {
                    List<T> tArrayList = new ArrayList<>();
                    return tArrayList;
                }
                try {
                    Class<? extends Object> tClass = one.getClass();
                    //整合出属性这个方法
                    Method m = tClass.getMethod("getSort");
                    //激活方法
                    sort = (Long) m.invoke(one);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //上一个的查询条件
                Long sortId = sort - (limit * finalPage);
                query.addCriteria(Criteria.where("sort").lte(sortId));
                resultList = mongodbUtils.mongoTemplate.find(query.limit(limit), clazz);
                Console.log("分页耗时" + (System.currentTimeMillis() - l));
                return resultList;
            }, executor);

            CompletableFuture<Map<String, Object>> all = future1.thenCombineAsync(future2, (f1, f2) -> {
                Map<String, Object> maps = new HashMap<>();
                maps.put("count", f1);
                maps.put("data", f2);
                return maps;
            }, executor);

            try {
                map = all.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            return map;
        }

    }

    /**
     * 根据条件查询出所有结果集
     *
     * @param tClass         泛型
     * @param keys           查询条件 key
     * @param values         查询条件 value
     * @param collectionName 集合名字
     * @return 信息
     */
    public <T> List<T> findDataByKeyLimit(Class<T> tClass, String[]
            keys, String[] values, String collectionName) {
        Criteria criteria = null;
        for (int i = 0; i < keys.length; i++) {
            if (i == 0) {
                criteria = Criteria.where(keys[i]).is(values[i]);
            } else {
                criteria.and(keys[i]).is(values[i]);
            }
        }
        Query query = Query.query(criteria);
        //全部结果信息
        return mongodbUtils.mongoTemplate.find(query, tClass, collectionName);

    }

    /**
     * 根据条件查询出分页结果集
     *
     * @param clazz  泛型
     * @param page   当前页
     * @param limit  分页条数
     * @param keys   查询条件 key
     * @param values 查询条件 value
     * @return 信息
     */
    public <T> Map<String, Object> findDataByKey(Class<T> clazz, String[]
            keys, String[] values, Integer page, Integer limit) {
        Map<String, Object> map = new HashMap<>();
        page = page - 1;
        Query query = new Query();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Criteria criteria = null;
        criteria.where("isDel").ne("1");
        for (int i = 0; i < keys.length; i++) {
            boolean status = keys[i].contains("Time");
            Pattern pattern = Pattern.compile("^" + values[i] + "", Pattern.CASE_INSENSITIVE);
            if (i == 0) {
                if (status) {
                    try {
                        criteria = Criteria.where(keys[i]).lte(sdf.parse(values[i].split(";")[1])).gte(sdf.parse(values[i].split(",")[0]));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                } else {
                    criteria = Criteria.where(keys[i]).regex(pattern);
                }
            } else {
                if (status) {
                    try {
                        criteria.and(keys[i]).lte(sdf.parse(values[i].split(";")[1])).gte(sdf.parse(values[i].split(",")[0]));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                } else {
                    criteria.and(keys[i]).regex(pattern);
                }
            }
        }
        query.addCriteria(criteria);

        //总量
        CompletableFuture<Long> future1 = CompletableFuture.supplyAsync(() -> {
            long l = System.currentTimeMillis();
            long count = this.mongoTemplate.count(query, clazz);
            System.out.println("获取总量用时--》" + (System.currentTimeMillis() - l));
            return count;
        }, executor);


        Integer finalPage = page;
        CompletableFuture<List<T>> future2 = CompletableFuture.supplyAsync(() -> {
            //根据集合中对象的某个字段排序
            query.with(Sort.by(
                    Sort.Order.desc("sort")
            ));
            List<T> resultList = new ArrayList<>();
            if (finalPage <= 1000) {
                resultList = mongodbUtils.mongoTemplate.find(query.limit(limit).skip(limit * finalPage), clazz);
            } else {
                Long sort = 0L;
                //获取最大值的sort
                T one = this.mongoTemplate.findOne(query, clazz);
                if (one == null) {
                    List<T> tArrayList = new ArrayList<>();
                    return tArrayList;
                }
                try {
                    Class<? extends Object> tClass = one.getClass();
                    //整合出属性这个方法
                    Method m = tClass.getMethod("getSort");
                    //激活方法
                    sort = (Long) m.invoke(one);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                //上一个的查询条件
                Long sortId = sort - (limit * finalPage);
                query.addCriteria(Criteria.where("sort").lte(sortId));
                resultList = mongodbUtils.mongoTemplate.find(query.limit(limit), clazz);
            }
            return resultList;
        }, executor);

        CompletableFuture<Map<String, Object>> all = future1.thenCombineAsync(future2, (f1, f2) -> {
            Map<String, Object> maps = new HashMap<>();
            maps.put("count", f1);
            maps.put("data", f2);
            return maps;
        }, executor);

        try {
            map = all.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 修改子集的删除字段为1，表示删除
     *
     * @param documentId 文档的id
     */
    public void updateDataIsDel(String documentId, Class<?> clazz) {
        Query query = new Query();
        query.addCriteria(Criteria.where("_id").is(documentId));
        Update update = new Update();
        update.set("isDel", "1");
        mongoTemplate.updateFirst(query, update, clazz);
    }


    /**
     * 根据集合对象中的id删除数据
     *
     * @param id     对象id
     * @param tClass 映射的集合
     */
    public void removeById(String id, Class<?> tClass) {
        try {
            mongodbUtils.mongoTemplate.remove(new Query(Criteria.where("_id").is(id)), tClass);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 删除子集文档的数据
     *
     * @param documentId  父级文档的id
     * @param childIdName 子集文档的唯一字段名称
     * @param childId     子集文档的唯一字段名称的值
     * @param childName   子集文档的名字
     * @param tClass      泛型
     * @return Result
     */
//    public Result removeChildDocument(String documentId, String
    public void removeChildDocument(String documentId, String
            childIdName, String childId, String childName, Class<?> tClass) {
        try {
            Query query = Query.query(Criteria.where("_id").is(documentId));
            BasicDBObject s = new BasicDBObject();
            s.put(childIdName, childId);
            Update update = new Update();
            update.pull(childName, s);
            mongodbUtils.mongoTemplate.updateFirst(query, update, tClass);
        } catch (Exception e) {
            e.printStackTrace();
//            return Result.error(500, "删除失败");
        }
//        return Result.ok();
    }


    /**
     * 根据key，value到指定集合删除数据
     *
     * @param key            键
     * @param value          值
     * @param collectionName 集合名
     */
//    public Result removeByKeyValue(String key, Object value, String
    public void removeByKeyValue(String key, Object value, String
            collectionName) {
        Criteria criteria = Criteria.where(key).is(value);
        criteria.and(key).is(value);
        Query query = Query.query(criteria);
        try {
            mongodbUtils.mongoTemplate.remove(query, collectionName);
        } catch (Exception e) {
            e.printStackTrace();
//            return Result.error(500, "删除失败");
        }
//        return Result.ok();
    }

    /**
     * 修改子集文档数组中内嵌文档指定一个元素的值
     *
     * @param documentId       父级文档的id
     * @param childIdName      子集文档的唯一字段名称
     * @param childId          子集文档的唯一字段名称的值
     * @param childName        子集文档的名字
     * @param updateChildName  要跟新的字段
     * @param updateChildValue 要跟新的值
     * @param tClass           泛型
     * @return Result
     */
//    public Result updateChildDocument(String documentId, String
    public void updateChildDocument(String documentId, String
            childIdName, String childId, String childName, String
                                            updateChildName, String updateChildValue, Class<?> tClass) {
        try {
            Query query = Query.query(Criteria.where("_id").is(documentId)
                    .and(childName + "." + childIdName).is(childId));
            Update update = new Update();
            update.set(childName + ".$." + updateChildName, updateChildValue);
            mongoTemplate.updateFirst(query, update, tClass);
        } catch (Exception e) {
//            return Result.error(500, "修改失败");
        }
//        return Result.ok();
    }

    /**
     * 向子集文档添加一个数据
     *
     * @param query       条件
     * @param childIdName 子集文档名字
     * @param o           元素对象
     * @param tClass      泛型
     * @return 结果
     */
    public void saveOneFieldChildDocument(Query query, String
            childIdName, Object o, Class<?> tClass) {
        try {
            Update update = new Update();
            update.addToSet(childIdName, o);
            mongodbUtils.mongoTemplate.upsert(query, update, tClass);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 保存数据对象，集合为数据对象中@Document 注解所配置的collection
     *
     * @param obj
     */
    public void saveData(Object obj) {
        try {
            mongodbUtils.mongoTemplate.save(obj);
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 指定集合保存数据对象
     *
     * @param obj            数据对象
     * @param collectionName 集合名
     */
    public void saveData(Object obj, String collectionName) {
        try {
            mongodbUtils.mongoTemplate.save(obj, collectionName);
        } catch (Exception e) {
            throw e;
        }
    }


    /**
     * 指定集合 修改数据，且仅修改找到的第一条数据
     *
     * @param accordingKey   修改条件 key
     * @param accordingValue 修改条件 value
     * @param updateKeys     修改内容 key数组
     * @param updateValues   修改内容 value数组
     * @param collectionName 集合名
     */
    public void updateFirst(String accordingKey, Object
            accordingValue, String[] updateKeys, Object[] updateValues,
                            String collectionName) {

        Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
        Query query = Query.query(criteria);
        Update update = new Update();
        for (int i = 0; i < updateKeys.length; i++) {
            update.set(updateKeys[i], updateValues[i]);
        }
        mongodbUtils.mongoTemplate.updateFirst(query, update, collectionName);
    }

    /**
     * 指定集合 修改数据，且修改所找到的所有数据
     *
     * @param accordingKey   修改条件 key
     * @param accordingValue 修改条件 value
     * @param updateKeys     修改内容 key数组
     * @param updateValues   修改内容 value数组
     * @param collectionName 集合名
     */
    public static void updateMulti(String accordingKey, Object
            accordingValue, String[] updateKeys, Object[] updateValues,
                                   String collectionName) {

        Criteria criteria = Criteria.where(accordingKey).is(accordingValue);
        Query query = Query.query(criteria);
        Update update = new Update();
        for (int i = 0; i < updateKeys.length; i++) {
            update.set(updateKeys[i], updateValues[i]);
        }
        mongodbUtils.mongoTemplate.updateMulti(query, update, collectionName);
    }


}
